package com.kelin.calendarlistview;

import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import ohos.multimodalinput.event.TouchEvent;

/**
 * 给组件设置触摸水波纹效果
 * 使用限制:如果该控件需要设置setTouchEventListener，水波纹效果会失效，
 * 需要使用该方法setComponentAndTouchEventListener
 * 在一些自定义组件处理触摸事件的复杂场景下，可能会有未知异常情况。
 */
public class RippleUtil {

    /**
     * 给指定组件设置触摸水波纹效果
     * <p>
     * 如果有setClickedListener，需要再此方法之前设置
     *
     * @param component   指定组件
     * @param velocity    水波扩散速度（默认值5）
     * @param rippleColor 水波纹颜色（默认值#aaaaaa）
     */
    public static void setComponent(Component component, int velocity, int rippleColor) {
        if (component == null) {
            return;
        }
        component.setTouchEventListener((component1, touchEvent) -> {
            addDrawTask(velocity, rippleColor, component1, touchEvent, null);
            return false;
        });
    }

    /**
     * 给指定组件设置触摸水波纹效果并且要自己处理触摸事件。
     *
     * @param component          指定组件
     * @param velocity           水波扩散速度（默认值5）
     * @param rippleColor        水波纹颜色（默认值#aaaaaa）
     * @param touchEventListener 触摸事件监听
     */
    public static void setComponentAndTouchEventListener(Component component, int velocity, int rippleColor, Component.TouchEventListener touchEventListener) {
        if (component == null || touchEventListener == null) {
            return;
        }
        component.setTouchEventListener((component1, touchEvent) -> {
            addDrawTask(velocity, rippleColor, component1, touchEvent, null);
            return touchEventListener.onTouchEvent(component1, touchEvent);
        });
    }

    /**
     * 给指定组件设置触摸水波纹效果并且要自己处理触摸事件。
     *
     * @param component          指定组件
     * @param touchEventListener 触摸事件监听
     */
    public static void setComponentAndTouchEventListener(Component component, Component.TouchEventListener touchEventListener) {
        setComponentAndTouchEventListener(component, 5, Color.getIntColor("#aaaaaa"), touchEventListener);
    }

    /**
     * 给指定组件设置触摸水波纹效果
     * 如果需要给指定组件设置setClickedListener，需要再此方法之前设置
     *
     * @param component 指定组件
     */
    public static void setComponent(Component component) {
        setComponent(component, 5, Color.getIntColor("#aaaaaa"));
    }


    /**
     * 取消水波纹效果
     *
     * @param component 指定组件
     */
    public static void cancelComponent(Component component) {
        if (component == null) {
            return;
        }
        component.setTouchEventListener(null);
    }


    /**
     * 给指定组件设置点击事件（带水波纹效果）
     *
     * @param component     指定组件
     * @param velocity      水波纹扩散速度
     * @param rippleColor   水波纹颜色
     * @param clickListener 监听点击事件
     */
    public static void setComponentClickListener(Component component, int velocity, int rippleColor, Component.ClickedListener clickListener) {
        if (component == null) {
            return;
        }
        component.setTouchEventListener((component1, touchEvent) -> {
            addDrawTask(velocity, rippleColor, component1, touchEvent, clickListener);
            return false;
        });
    }


    /**
     * 给指定组件设置点击事件（带水波纹效果）
     *
     * @param component          指定组件
     * @param velocity           水波纹扩散速度
     * @param rippleColor        水波纹颜色
     * @param clickListener      监听点击事件
     * @param touchEventListener 触摸事件监听
     */
    public static void setComponentClickListener(Component component, int velocity, int rippleColor, Component.TouchEventListener touchEventListener, Component.ClickedListener clickListener) {
        if (component == null || touchEventListener == null) {
            return;
        }
        component.setTouchEventListener((component1, touchEvent) -> {
            addDrawTask(velocity, rippleColor, component1, touchEvent, clickListener);
            return touchEventListener.onTouchEvent(component1, touchEvent);
        });
    }

    /**
     * 给指定组件设置点击事件（带水波纹效果）
     *
     * @param component     指定组件
     * @param clickListener 监听点击事件
     */
    public static void setComponentClickListener(Component component, Component.ClickedListener clickListener) {
        setComponentClickListener(component, 5, Color.getIntColor("#aaaaaa"), clickListener);
    }

    private static void addDrawTask(int velocity, int rippleColor, Component component1, TouchEvent touchEvent, Component.ClickedListener clickedListener) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
            RippleDrawTask rippleDrawTask = new RippleDrawTask(new Point(touchEvent.getPointerPosition(touchEvent.getIndex()).getX(),
                    touchEvent.getPointerPosition(touchEvent.getIndex()).getY()),
                    component1,
                    rippleColor);

            rippleDrawTask.setVelocity(velocity);
            if (clickedListener != null) {
                component1.setClickedListener(new Component.ClickedListener() {
                    @Override
                    public void onClick(Component component) {
                        component1.addDrawTask(rippleDrawTask);
                        component1.invalidate();
                        clickedListener.onClick(component);
                    }
                });
            } else {
                component1.addDrawTask(rippleDrawTask);
                component1.invalidate();
            }
        }
    }

    private static class RippleDrawTask implements Component.DrawTask, Runnable, Component.BindStateChangedListener {
        private final Paint paint;
        private final RectFloat rectFloat;
        private final Point centerPoint;
        private final Component component;
        private final EventHandler eventHandler;

        private float maxRadius;

        private int times = 1;

        private int velocity;

        private boolean isUnBind = false;

        /**
         * 水波纹出现时的起始半径
         */
        private float startingRadius;

        /**
         * setStartingRadius
         * @param startingRadius
         */
        public void setStartingRadius(float startingRadius) {
            this.startingRadius = startingRadius;
        }

        /**
         * setVelocity
         * @param velocity
         */
        public void setVelocity(int velocity) {
            this.velocity = velocity;
        }


        private RippleDrawTask(Point centerPoint, Component component, int rippleColor) {
            paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.FILL_STYLE);
            paint.setColor(new Color(rippleColor));
            rectFloat = new RectFloat();
            this.centerPoint = centerPoint;
            eventHandler = new EventHandler(EventRunner.getMainEventRunner());
            this.component = component;
            component.setBindStateChangedListener(this);
        }

        private void calculateMaxRadius(Component component) {
            if (maxRadius >= 0.001f) {
                return;
            }
            int height = component.getHeight();
            int width = component.getWidth();
            // 最大半径
            maxRadius = Math.max(Math.max(centerPoint.getPointX(), Float.sum(-centerPoint.getPointX(), width)),
                    Math.max(centerPoint.getPointY(), Float.sum(height, -centerPoint.getPointY())));
            maxRadius = maxRadius * 4 / 3;

            if (startingRadius <= 0.001f) {
                //默认是从最大半径的一半位置开始往外扩散
                startingRadius = maxRadius * 0.5f;
            }
        }

        @Override
        public void onDraw(Component component, Canvas canvas) {
            if (isUnBind) {
                return;
            }
            calculateMaxRadius(component);
            updateRadiusAndAlpha();
            canvas.drawOval(rectFloat, paint);
            if (paint.getAlpha() <= 0.001f) {
                return;
            }
            eventHandler.postTask(this);
        }

        private void updateRadiusAndAlpha() {
            float radius = timeLine();
            rectFloat.left = Float.sum(centerPoint.getPointX(), -radius);
            rectFloat.top = Float.sum(centerPoint.getPointY(), -radius);
            rectFloat.right = Float.sum(centerPoint.getPointX(), radius);
            rectFloat.bottom = Float.sum(centerPoint.getPointY(), radius);
            if (radius > maxRadius) {
                radius = maxRadius;
            }
            paint.setAlpha(Float.sum(1, -radius / maxRadius));
        }

        /**
         * 这是动画执行速率线
         * 默认是加速动画，需要定制可以修改该方法的算法
         *
         * @return 当前波纹半径
         */
        private float timeLine() {
            float value = Float.sum(startingRadius, velocity * (times * times * 0.5f));
            times++;
            return value;
        }

        @Override
        public void run() {
            component.invalidate();
        }

        @Override
        public void onComponentBoundToWindow(Component component) {

        }

        @Override
        public void onComponentUnboundFromWindow(Component component) {
            isUnBind = true;
            if (eventHandler != null) {
                eventHandler.removeTask(this);
            }
        }
    }
}